/*!
 * \file machine.c
 * \brief Gestion de la machine
 * \author Jérôme Brunel
 * \version 1.0
 * \date 09 juin 2011
 */
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include "machine.h"
#include "exec.h"
#include "debug.h"

//! Ecrit dans le fichier binaire dump.bin
/*!
 * \param pmach la machine en cours d'éxécution
 */
void ecrireBinaire(Machine *pmach) {
    FILE *f = fopen("dump.bin", "wb");
    if(f==NULL) {
        fprintf(stderr, "Impossible de créer le fichier binaire dump.bin.\n");
        return;
    }
    int i;
    unsigned int textsize = pmach->_textsize;
    unsigned int datasize = pmach->_datasize;
    unsigned int dataend = pmach->_dataend;
    Instruction *text = pmach-> _text;
    Word *data = pmach->_data;
    
    fwrite(&textsize, sizeof(uint32_t), 1, f);
    fwrite(&datasize, sizeof(uint32_t), 1, f);
    fwrite(&dataend, sizeof(uint32_t), 1, f);
    for(i=0; i<textsize; i++)
        fwrite(&(text[i]._raw), sizeof(uint32_t), 1, f);
    fwrite(data, sizeof(uint32_t), datasize, f);
    
    fclose(f);
}

//! Associe le chiffre du code condition à la lettre
/*!
 * \param cc le code condition
 * \return la lettre correspondante au paramètre cc
 */
char getCC(Condition_Code cc) {
	switch(cc) {
		case CC_U: return 'U'; break;
		case CC_Z: return 'Z'; break;
		case CC_P: return 'P'; break;
		case CC_N: return 'N'; break;
	}
	return LAST_CC;
}

void load_program(Machine *pmach, unsigned textsize, Instruction text[textsize], unsigned datasize, Word data[datasize], unsigned dataend){
	int i;
	pmach->_text = text;
	pmach->_textsize = textsize;
	pmach->_data = data;
	pmach->_datasize = datasize;
	pmach->_dataend = dataend;
	pmach->_pc = 0;
	pmach->_cc = CC_U;
	for(i=0; i<NREGISTERS; i++) {
		pmach->_registers[i] = 0;
	}
	pmach->_sp = datasize-1;
}

void read_program(Machine *pmach, const char *programfile){
    FILE *f = fopen(programfile, "rb");
    if(f==NULL) {
        fprintf(stderr, "Impossible de lire le fichier binaire %s.\n", programfile);
        return;
    }
    int i;
    unsigned int textsize, datasize, dataend;
    
    fread(&textsize, sizeof(uint32_t), 1, f);
    fread(&datasize, sizeof(uint32_t), 1, f);
    fread(&dataend, sizeof(uint32_t), 1, f);
    
    Instruction *text = malloc(textsize*sizeof(Instruction));
    Word *data = malloc(datasize*sizeof(Word));
    for(i=0; i<textsize; i++)
        text[i]._raw = 0;
        
    for(i=0; i<textsize; i++)
        fread(&(text[i]._raw), sizeof(uint32_t), 1, f);
    fread(data, sizeof(uint32_t), datasize, f);
    
    fclose(f);
    
    load_program(pmach, textsize, text, datasize, data, dataend);
}

void dump_memory(Machine *pmach){
    int i;
	printf("Instruction text[] = {\n");
	for(i=0; i<pmach->_textsize; i++) {
	    if(i%4==0) putchar('\t');
	    printf("%s%#8.8x, ", (pmach->_text[i]._raw==0?"0x":""), pmach->_text[i]._raw);
	    if(i%4==3) putchar('\n');
	}
	printf("\n");
	printf("};\n");
	printf("unsigned textsize = %d;\n\n", pmach->_textsize);
	printf("Word data[] = {\n");
	for(i=0; i<pmach->_datasize; i++) {
	    if(i%4==0) putchar('\t');
	    printf("%s%#8.8x, ", (pmach->_data[i]==0?"0x":""), pmach->_data[i]);
	    if(i%4==3) putchar('\n');
	}
	printf("};\n");
	printf("unsigned datasize = %d;\n", pmach->_datasize);
	printf("unsigned dataend = %d;\n", pmach->_dataend);
	
	ecrireBinaire(pmach);
}

void print_program(Machine *pmach){
	int i;
	printf("*** PROGRAM (size: %d) ***\n", pmach->_textsize);
	for(i=0; i<pmach->_textsize; i++) {
		printf("%s%#4.4x: %s%#8.8x", (i==0?"0x":""), i, (pmach->_text[i]._raw==0?"0x":""), pmach->_text[i]._raw);
		putchar('\t');
		print_instruction(pmach->_text[i], i);
		putchar('\n');
	}
	putchar('\n');
}

void print_data(Machine *pmach){
	int i;
	printf("*** DATA (size: %d, end = %#8.8x (%d)) ***\n", pmach->_datasize, pmach->_dataend, pmach->_dataend);
	for(i=0; i<pmach->_datasize; i++) {
		printf("%s%#4.4x: %s%#8.8x %d%s", (i==0?"Ox":""), i, (pmach->_data[i]==0?"Ox":""), pmach->_data[i], pmach->_data[i], (i%3==2?"\n":"\t"));
	}
	printf("\n\n");
}

void print_cpu(Machine *pmach){
	int i;
	printf("\n*** CPU ***\nPC: %s%#8.8x\tCC: %c\n\n", (pmach->_pc==0?"0x":""), pmach->_pc, getCC(pmach->_cc));
	for(i=0; i<NREGISTERS; i++) {
		printf("R%s%d: %s%#8.8x %d%s", (i<10?"0":""), i, (pmach->_registers[i]==0?"0x":""), pmach->_registers[i], pmach->_registers[i], (i%3==2?"\n":"\t"));
	}
	printf("\n\n");
}

void simul(Machine *pmach, bool debug){
    Instruction instruct;
    while(1) {
        instruct = pmach->_text[pmach->_pc];
        trace("Executing", pmach, instruct, pmach->_pc++);
        if(!decode_execute(pmach, instruct))
            return;
        if(debug)
            debug_ask(pmach);
    }
}
